using System;
using System.Linq;
using System.Reflection;
using Andshev.OrmTools.Linq2Query.Attributes;
using Andshev.OrmTools.Linq2Query.Enumerations;

namespace Andshev.OrmTools.Linq2Query.Tools
{
    public class ReflectionTool
    {
        #region Property and attribute reflection

        #region GetAttributedProperties

        public static PropertyInfo[] GetAttributedProperties(Type type, Type attributeType)
        {
            return type.GetProperties()
                .Where(p => p.GetCustomAttributes(attributeType, true).Length > 0)
                .ToArray();
        }

        public static PropertyInfo[] GetAttributedProperties<T, TAttribute>()
        {
            return GetAttributedProperties(typeof(T), typeof(TAttribute));
        }

        public static PropertyInfo GetProperty<T>(string propertyName)
        {
            return typeof(T).GetProperty(propertyName);
        }

        #endregion

        #region GetAttribute

        public static TAttribute GetAttribute<TAttribute>(PropertyInfo property)
        {
            return (TAttribute)property.GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault();
        }

        public static TAttribute GetAttribute<TType, TAttribute>()
        {
            return (TAttribute)typeof(TType).GetCustomAttributes(typeof(TAttribute), true).FirstOrDefault();
        }

        #endregion

        #endregion

        #region Column reflection

        #region GetColumns

        public static PropertyInfo[] GetColumns<TEntity>()
        {
            return GetColumns(typeof(TEntity));
        }

        public static PropertyInfo[] GetColumns(Type type)
        {
            return GetAttributedProperties(type, typeof(ColumnAttribute));
        }

        #endregion

        #region GetForeignEntityColumns

        public static PropertyInfo[] GetForeignEntityColumns<TEntity>()
        {
            return GetForeignEntityColumns(typeof(TEntity));
        }

        public static PropertyInfo[] GetForeignEntityColumns(Type type)
        {
            return GetAttributedProperties(type, typeof(ForeignEntityAttribute));
        }

        #endregion

        #region GetIdentityColumnName

        public static string GetIdentityColumnName(object entity)
        {
            return GetIdentityColumnName(entity.GetType());
        }

        public static string GetIdentityColumnName(Type type)
        {
            return
                type.GetProperties().FirstOrDefault(
                    p => GetColumnAttribute(p).ColumnType == ColumnType.Identity
                )
                .Name;
        }

        #endregion

        #region GetColumnAttribute

        public static ColumnAttribute GetColumnAttribute(PropertyInfo property)
        {
            return (ColumnAttribute)property.GetCustomAttributes(typeof(ColumnAttribute), true)
                .FirstOrDefault();
        }

        #endregion

        #endregion

        #region Mapping reflection

        public static TableMappingAttribute GetTableMapping<T>()
        {
            return (TableMappingAttribute)typeof(T).GetCustomAttributes(typeof(TableMappingAttribute), true)
                                                   .FirstOrDefault();
        }

        #endregion

        #region Get/Set IdentityValue

        public static int GetIdentityValue(object entity)
        {
            return (int)GetColumns(entity.GetType())
                .SingleOrDefault(p => GetColumnAttribute(p).ColumnType == ColumnType.Identity)
                .GetValue(entity, null);
        }

        public static void SetIdentityValue(object entity, int id)
        {
            GetColumns(entity.GetType())
                .SingleOrDefault(p => GetColumnAttribute(p).ColumnType == ColumnType.Identity)
                .SetValue(entity, id, null);
        }

        #endregion
    }
}